package com.zh.freechat.domain.auth;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import static com.zh.webcommon.CommonConst.DAY;


/**
 * @author ZH
 * @date 17:16 2021/8/1
 */
@Slf4j
@Component
public class JwtService {
    //用于签名的访问/刷新令牌的密钥
    private SecretKey secretKey;
    private SecretKey refreshSecretKey;

    /**
     * 访问令牌有效时间(s)
     */
    public static final int token_expire = DAY; //1天
    /**
     * 刷新令牌有效时间(s)
     */
    public static final int refresh_token_expire = DAY * 5; //5天

    public JwtService(@Value("${access-token-secretKey}") String secretKey
            , @Value("${refresh-token-secretKey}") String refreshSecretKey) {
        log.info("[JwtService] constructor ...");

        // 从字符串还原密钥（用于 JWT 签名/验证）
        this.secretKey = Keys.hmacShaKeyFor(Base64.getDecoder().decode(secretKey));
        this.refreshSecretKey = Keys.hmacShaKeyFor(Base64.getDecoder().decode(refreshSecretKey));
    }


    public static void main(String[] args) {
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        var plain = Base64.getEncoder().encodeToString(secretKey.getEncoded());
        // 生成并存储密钥字符串（实际项目中存入配置文件）
        System.out.println("存储的密钥字符串：" + plain);
    }

    /**
     * 生成jwtToken
     *
     * @param user
     * @return
     */
    public String createJwtToken(LoginUser user) {
        Date nowTime = new Date();
        //+30分钟
        Instant instant = nowTime.toInstant().plusSeconds(token_expire);
        Date expireTime = Date.from(instant);

        var builder = Jwts.builder()
                .claim("username", user.getUsername())
                .claim("name", user.getName())
                .claim("avatar", user.getAvatar())
                .claim("roles", user.getRoles())
                .setId(UUID.randomUUID().toString())
                .setSubject(user.getUid()) //用户ID
                .setIssuer("mall")
                .setIssuedAt(nowTime)
                .setExpiration(expireTime)
                .signWith(secretKey);

        if (user.getOpenId() != null) {
            builder.claim("openId", user.getOpenId());
        }
        return builder.compact();
    }

    /**
     * 生成jwtToken
     *
     * @param username
     * @return
     */
    public String createRefreshToken(String userId, String username) {
        Date nowTime = new Date();
        Instant instant = nowTime.toInstant().plusSeconds(refresh_token_expire);
        Date expireTime = Date.from(instant);

        return Jwts.builder()
                .setId(UUID.randomUUID().toString())
                .setSubject(userId)
                .setIssuer("mall")
                .setIssuedAt(nowTime)
                .setExpiration(expireTime)
                .signWith(refreshSecretKey)
                .compact();
    }

    /**
     * 解析jwtToken
     *
     * @param token
     * @return uid
     */
    public String parseRefreshToken(String token) {
        JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
        Claims claims = jwtParser.parseClaimsJws(token).getBody();

        return claims.getSubject();
    }

    /**
     * 解析jwtToken
     *
     * @param token
     * @return
     */
    public LoginUser parseJwtToken(String token) {
        JwtParser jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
        Claims claims = jwtParser.parseClaimsJws(token).getBody();

        return rebuildUserInfo(claims);
    }

    static LoginUser rebuildUserInfo(Claims claims) {
        var uid = claims.getSubject();
        var username = (String) claims.get("username");
        var name = (String) claims.get("name");
        var avatar = (String) claims.get("avatar");
        var openId = (String) claims.get("openId");

        List<String> roles = (List<String>) claims.get("roles");

        return new LoginUser(uid, username, name, avatar, roles, openId);

    }

}
